/*******************************************************************************
 * Copyright (c) 2025 Eurotech and/or its affiliates and others
 *
 * This program and the accompanying materials are made
 * available under the terms of the Eclipse Public License 2.0
 * which is available at https://www.eclipse.org/legal/epl-2.0/
 *
 * SPDX-License-Identifier: EPL-2.0
 *
 * Contributors:
 *  Eurotech
 ******************************************************************************/
// Content with portions generated by generative AI platform
package org.eclipse.kura.core.crypto;

import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;

import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Optional;

import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;

public class SystemdCredentialLoaderTest {

    @Rule
    public TemporaryFolder tempFolder = new TemporaryFolder();

    private Path credentialRootDirectory;
    private SystemdCredentialLoader loader;
    private Optional<SystemdCredentialLoader> result;
    private byte[] credentialData;
    @Test
    public void createFromEnvironmentVariableSuccess() {
        givenCredentialRootDirectory();

        whenCreatingLoaderFromEnv();

        thenLoaderMayOrMayNotBePresent();
    }

    @Test
    public void createFromEnvironmentVariableNotSet() {
        givenNoEnvironmentVariable();

        whenCreatingLoaderFromEnv();

        thenLoaderIsNotPresent();
    }

    @Test
    public void loadExistingCredential() {
        givenCredentialRootDirectory();
        givenCredentialFile("test-credential", "secret-value");
        givenLoader();

        whenLoadingCredential("test-credential");

        thenCredentialDataEquals("secret-value");
    }

    @Test
    public void loadCredentialWithBinaryData() {
        givenCredentialRootDirectory();
        givenCredentialFileWithBytes("binary-cred", new byte[]{0x01, 0x02, 0x03, 0x04});
        givenLoader();

        whenLoadingCredential("binary-cred");

        thenCredentialDataEquals(new byte[]{0x01, 0x02, 0x03, 0x04});
    }

    @Test
    public void loadNonExistentCredential() {
        givenCredentialRootDirectory();
        givenLoader();

        whenLoadingCredential("non-existent");

        thenCredentialIsNotPresent();
    }

    @Test
    public void loadCredentialFromInvalidDirectory() {
        givenNonExistentCredentialDirectory();
        givenLoader();

        whenLoadingCredential("any-credential");

        thenCredentialIsNotPresent();
    }

    @Test
    public void loadEmptyCredential() {
        givenCredentialRootDirectory();
        givenCredentialFile("empty-cred", "");
        givenLoader();

        whenLoadingCredential("empty-cred");

        thenCredentialDataIsEmpty();
    }

    private void givenCredentialRootDirectory() {
        try {
            this.credentialRootDirectory = tempFolder.newFolder().toPath();
        } catch (IOException e) {
            fail("Failed to create temporary directory");
        }
    }

    private void givenNonExistentCredentialDirectory() {
        this.credentialRootDirectory = tempFolder.getRoot().toPath().resolve("non-existent");
    }

    private void givenNoEnvironmentVariable() {
        // Environment variables cannot be set in Java tests, so this is a no-op
    }

    private void givenCredentialFile(String name, String content) {
        try {
            Path credFile = this.credentialRootDirectory.resolve(name);
            Files.write(credFile, content.getBytes(StandardCharsets.UTF_8));
        } catch (IOException e) {
            fail("Failed to create credential file");
        }
    }

    private void givenCredentialFileWithBytes(String name, byte[] content) {
        try {
            Path credFile = this.credentialRootDirectory.resolve(name);
            Files.write(credFile, content);
        } catch (IOException e) {
            fail("Failed to create credential file");
        }
    }

    private void givenLoader() {
        this.loader = new SystemdCredentialLoader(this.credentialRootDirectory.toFile());
    }

    private void whenCreatingLoaderFromEnv() {
        this.result = SystemdCredentialLoader.fromEnv();
    }

    private void whenLoadingCredential(String name) {
        try {
            this.credentialData = this.loader.loadCredential(name).orElse(null);
        } catch (IOException e) {
            fail("Unexpected exception: " + e.getMessage());
        }
    }

    private void thenLoaderIsPresent() {
        assertNotNull(this.result);
        assertTrue(this.result.isPresent());
    }

    private void thenLoaderIsNotPresent() {
        assertNotNull(this.result);
        assertFalse(this.result.isPresent());
    }

    private void thenLoaderMayOrMayNotBePresent() {
        assertNotNull(this.result);
    }

    private void thenCredentialIsNotPresent() {
        assertTrue("Expected credential data to be null for non-existent credentials", this.credentialData == null);
    }

    private void thenCredentialDataEquals(String expected) {
        assertNotNull(this.credentialData);
        assertArrayEquals(expected.getBytes(StandardCharsets.UTF_8), this.credentialData);
    }

    private void thenCredentialDataEquals(byte[] expected) {
        assertNotNull(this.credentialData);
        assertArrayEquals(expected, this.credentialData);
    }

    private void thenCredentialDataIsEmpty() {
        assertNotNull(this.credentialData);
        assertEquals(0, this.credentialData.length);
    }
}
